/*
**************************************************************************************
*Myriad Engine                                                                       *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
package leola.lang;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import leola.frontend.Leola;
import leola.frontend.LeolaLibrary;
import leola.types.LeoArray;
import leola.types.LeoFunction;
import leola.types.LeoNativeClass;
import leola.types.LeoNull;
import leola.types.LeoObject;
import leola.types.LeoString;
import leola.types.LeoObject.LeoType;

/**
 * The system core functions
 * 
 * @author Tony
 *
 */
public class SystemLeolaLibrary implements LeolaLibrary {

	/**
	 * Object
	 */
//	private static final Object mutex = new Object();
	
	/**
	 * Leola
	 */
//	private static Leola leola;
	
	/* (non-Javadoc)
	 * @see leola.frontend.LeolaLibrary#init(leola.frontend.Leola)
	 */
	@Override
	public void init(Leola aLeola) throws Exception {
//		synchronized (mutex) {		
//			if ( leola == null ) {
//				leola = aLeola;
//				
//			}		
//		}
		
		aLeola.getInterpreter().loadClass(SystemLeolaLibrary.class);
		aLeola.eval(SystemLeolaLibrary.class.getResourceAsStream("system.leola"));
	}
	
	/**
	 * Loads a library
	 * 
	 * @param lib
	 */
	public static final void __loadLibrary(Leola leola, String lib) throws Exception {
		leola.loadLibrary(Class.forName(lib));
	}
	
	/**
	 * Loads a class
	 * 
	 * @param className
	 * @throws Exception
	 */
	public static final void __import(Leola leola, String className) throws Exception {
		try {
			leola.getInterpreter().loadClass(className);
		}
		catch(Exception e) {
			java.lang.System.err.println(e);
		}
	}
	
	/**
	 * Reads a line from sys in
	 * 
	 * TODO - move to io
	 * @return
	 */
	public static final String readln() {
		String result = null;
		Scanner s = new Scanner(java.lang.System.in);
		try {
			if ( s.hasNext() ) {
				result = s.nextLine();
			}
		}
		finally {
			s.close();
		}
		
		return result;
	}
	
	/**
	 * Prints to the console with a new line
	 * 
	 * @param x
	 */
	public static final void println(Object x) {
		java.lang.System.out.println(x);
	}
	
	/**
	 * Prints to the console.
	 * 
	 * @param x
	 */
	public static final void print(Object x) {
		java.lang.System.out.print(x);
	}
	
	/**
	 * Transforms the supplied object into a number
	 * @param x
	 * @return
	 */
	public static final double toNumber(Object x) {
		String str = x.toString();
		return Double.parseDouble(str);
	}
	
	/**
	 * Transforms the supplied object into a string
	 * @param x
	 * @return
	 */
	public static final String toString(Object x) {
		return x.toString();
	}
	
	public static final LeoNativeClass toBytes(String str) {
		return new LeoNativeClass(byte[].class, str.getBytes());
	}
	
	/**
	 * @param x
	 * @return an integer representation
	 */
	public static final int toInt(Object x) {
		Double d = toNumber(x);
		return d.intValue();
	}
	
	/**
	 * returns the length of the array
	 * 
	 * @param array
	 * @return
	 */
	public static final int length(LeoObject array) {
		if ( array == null ) return 0;
		if ( array.isOfType(LeoType.NULL)) return 0;
		if ( array.isOfType(LeoType.STRING)) return ((LeoString)array.as()).getString().length();
		if ( array.isOfType(LeoType.ARRAY)) return ((LeoArray)array.as()).size();
		
		throw new IllegalArgumentException("Illegal Argument type: " + array);
	}
	
	/**
	 * Constructs a new Array
	 * @param size
	 * @return
	 */
	public static final LeoArray newArray(int size) {
		List<LeoObject> array = new ArrayList<LeoObject>(size);
		for(int i = 0; i < size; i++ ) {
			array.add(LeoNull.LEONULL);
		}
		return new LeoArray(array);
	}
	

	
	/**
	 * Returns a new thread with the supplied {@link LeoFunction} serving
	 * as the thread runnable.
	 * 
	 * @param function
	 * @return
	 */
	public static final LeoNativeClass __newThread(final Leola leola, final LeoFunction function) {
		Thread thread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				try {					
					leola.getInterpreter().executeFunction(function);
				} 
				catch (Throwable t) {
					t.printStackTrace();
				}	
			}
		});
		
		return new LeoNativeClass(thread);
	}
	
	/**
	 * Sleeps
	 * 
	 * @param time
	 * @throws Exception
	 */
	public static final void sleep(long time) throws Exception {
		Thread.sleep(time);
	}
	
	/**
	 * Exits the JVM
	 * 
	 * @param code
	 * @throws Exception
	 */
	public static final void exit(int code) throws Exception {
		java.lang.System.exit(code);
	}
	
	/**
	 * @return the current time in milliseconds
	 * @throws Exception
	 */
	public static final long time() throws Exception {
		return System.currentTimeMillis();
	}
}
